
// UserLogin Function to Search and Post Mastodon Status
class MastodonUserManager {
    constructor(instanceUrl, accessToken, accountUrl) {
        this.instanceUrl = instanceUrl;
        this.accessToken = accessToken;
        this.accountUrl = accountUrl;
    }
    
    available() {
        return this.instanceUrl && this.accessToken && this.accountUrl
    }

    // search for a status
    async searchStatus(otherStatusURL) {
        const response = await fetch(`${this.instanceUrl}/api/v2/search?q=${encodeURIComponent(otherStatusURL)}&resolve=true&type=statuses`, {
            method: 'GET',
            headers: { 'Authorization': `Bearer ${this.accessToken}` }
        });
        if (!response.ok) {
            throw new Error(`Error searching for status: ${response.statusText}`);
        }
        const data = await response.json();
        return data;
    }

    // post a new status
    async postStatus(status) {
        const response = await fetch(`${this.instanceUrl}/api/v1/statuses`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${this.accessToken}`,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(status)
        });
        if (!response.ok) {
            throw new Error(`Error posting status: ${response.statusText}`);
        }
        const data = await response.json();
        return data;
    }

    isMyStatus(status) {
        return status.account.url === this.accountUrl;
    }

    async replyStatus(status_url, reply_text, autoAt = true) {
        try {
            // Search for a status and get id
            const searchResults = await searchStatus(status_url);
            const in_reply_status = searchResults.statuses[0]
            let status = { status: reply_text, in_reply_to_id: in_reply_status.id };
            // Not myself, @it
            if ((in_reply_status.account.url != this.accountUrl) && autoAt) {
                status.status = mention ? `@${in_reply_status.account.acct} ${reply_text}` : reply_text
                status.visibility = "unlisted"
            } else if (!autoAt) {
                status.visibility = "unlisted"
            }
            // Post a new status
            const postedStatus = await postStatus(status);
            return postedStatus;
        } catch (error) {
            console.error(error.message);
            return null;
        }
    }
}


// To show the comments , Website Owner Need to Provide A Bot AccessToken with scope is 'read'
// Need url such as https://some.com/@tom/statuses/xxxx and botAccesstoken
class MastodonBot {
    constructor(mainStatusURL, botAccessToken) {
        this.mainStatusURL = mainStatusURL;
        this.botAccessToken = botAccessToken;
        this.tootContainer = document.getElementById("toot-container");
        this.commentsContainer = document.getElementById("comments-container");
    }
    // Function to fetch and display the toot and comments 
    async fetchTootAndComments(timeFmt) {
        let apiUrl = this.mainStatusURL.replace(/\/@.*?\//g, "/api/v1/statuses/");
        apiUrl = apiUrl.replace(/statuses\/statuses/g , "statuses")
        try {
            const [mainResponse, commentResponse] = await Promise.all([
                fetch(apiUrl, { headers: { "Authorization": `Bearer ${this.botAccessToken}` } }),
                fetch(apiUrl + "/context", { headers: { "Authorization": `Bearer ${this.botAccessToken}` } })
            ]);
            const [mainData, commentData] = await Promise.all([
                mainResponse.json(),
                commentResponse.json()
            ]);

            // Display the Main toot
            this.tootContainer.innerHTML = `
                    <a href="${mainData.account.url}"><strong>${mainData.account.display_name}</strong> <small>@${mainData.account.acct}</small></a>
                    <p>${mainData.content}</p>
                    <small style="display: flex; justify-content: flex-end;">${timeFmt(mainData.created_at).twitter()} | <a href="${mainData.url}">&nbsp;Reply</a></small>
                    <div><a href="${mainData.url}">Raw Toot</a></div>
                `;

            // Display comments
            this.commentsContainer.innerHTML = "";
            let sortedNyULID = commentData.descendants.sort((a, b) => a.id.localeCompare(b.id));
            sortedNyULID.forEach(comment => {
                let retoot = commentData.descendants.find(({ id }) => id === comment.in_reply_to_id)
                if (retoot) {
                    this.commentsContainer.innerHTML += `
                        <div class="comment">
                            <a href="${comment.account.url}"><strong>${comment.account.display_name}</strong> <small>@${comment.account.acct}</small></a>
                            <blockquote>> <a href="${retoot["account"].url}"><strong>${retoot["account"].display_name}</strong> <small>@${retoot["account"].acct}</small></a> : ${retoot["content"]} </blockquote>
                            ${comment.content}
                            <small style="display: flex; justify-content: flex-end;">${timeFmt(comment.created_at).twitter()} | <a href="${comment.url}">&nbsp;Reply</a></small>
                            <hr style="margin: 0.5rem 0"/>
                        </div>
                        `;
                } else {
                    this.commentsContainer.innerHTML += `
                        <div class="comment">
                            <a href="${comment.account.url}"><strong>${comment.account.display_name}</strong> <small>@${comment.account.acct}</small></a>
                            ${comment.content}
                            <small style="display: flex; justify-content: flex-end;">${timeFmt(comment.created_at).twitter()} | <a href="${comment.url}">&nbsp;Reply</a></small>
                            <hr style="margin: 0.5rem 0"/>
                        </div>
                        `;
                }
            });
        } catch (error) {
            this.commentsContainer.innerHTML += `<div>${error}</div>`
            console.error("Error fetching data:", error);
        }
    }
}

// Manager Page Comments Need Input id: statusInput
class MastodonComment {
    constructor(mastodonBot, mastodonUser, commentInfo, timeFmt) {
        this.mastodonBot = mastodonBot;
        this.mastodonUser = mastodonUser;
        this.commentInfo = commentInfo;
        this.timeFmt = timeFmt;
        this.mainStatus = null;
        this.replyStatus = null;
        this.statusInput = document.getElementById('statusInput');
        this.statusSubmitButton = document.getElementById('submitBtn');
    }

    // LoginUser Search mainStatus with LoginUser's server
    mainStatusSearch() {
        if (!this.mastodonUser.available()) { this.commentInfo("Not Login"); return; }
        this.commentInfo("Searching Main Status...");
        this.mastodonUser.searchStatus(this.mastodonBot.mainStatusURL).then(res => {
            this.commentInfo("Reply Available. Reply: Main Status");
            this.mainStatus = res.statuses[0];
            this.replyStatus = this.mainStatus;
            if (!this.mastodonUser.isMyStatus(this.replyStatus)) {
                this.commentInfo("Reply Available. Reply: Main Status: " + this.replyStatus.id + " Not my status");
                this.statusInput.value = `@${this.replyStatus.account.acct} ${this.statusInput.value}`
            }
        }).catch(error => {
            this.commentInfo("Reply Unavailable. Search Main Status with your account server error" + error.message);
        });
    }

    init() {
        const ctx = this;
        // Submit 
        this.statusSubmitButton.addEventListener('click', async () => {
            if (!this.mastodonUser.available()) { this.commentInfo("Not Login"); return; }
            let status = { status: this.statusInput.value, in_reply_to_id: this.replyStatus.id };
            if (!this.mastodonUser.isMyStatus(this.replyStatus)) {
                status.visibility = "unlisted"
            }
            if (this.statusInput.value.trim() !== '') {
                this.commentInfo("Status POST")
                this.mastodonUser.postStatus(status).then(res => {
                    ctx.commentInfo("Status POST Successfully")
                    ctx.replyStatus = ctx.mainStatus;
                    ctx.statusInput.value = ''; 
                    ctx.mastodonBot.fetchTootAndComments(this.timeFmt);
                }).catch(error => {
                    this.commentInfo("Status POST Error: " + error.message)
                })
            }
        });
        // Replay Click
        document.addEventListener('click', function (event) {
            if (event.target.tagName === 'A') {
                if (event.target.textContent === " Reply") {
                    event.preventDefault();  // 阻止默认跳转行为
                    if (!ctx.mastodonUser.available()) { ctx.commentInfo("Not Login"); return; }
                    ctx.commentInfo("Reply Searching... " + event.target.href);
                    ctx.mastodonUser.searchStatus(event.target.href).then(res => {
                        ctx.replyStatus = res.statuses[0];
                        ctx.commentInfo("Reply: " + res.statuses[0].id);
                        if (!ctx.mastodonUser.isMyStatus(ctx.replyStatus)) {
                            ctx.commentInfo("Reply: " + ctx.replyStatus.id + "\nNot my status");
                            ctx.statusInput.value = `@${ctx.replyStatus.account.acct} ${ctx.statusInput.value}`
                        }
                    }).catch(error => {
                        this.commentInfo("Reply Search " + event.target.href + "Error: " + error.message)
                    })
                }
            }
        });
        // 
        this.mastodonBot.fetchTootAndComments(this.timeFmt);
        this.mainStatusSearch();
        if (!this.mastodonUser.available()) { this.commentInfo("Not Login");}
    }
}